package com.example.moudlelibrary.controller;

import com.example.moudlelibrary.dao.*;
import com.example.moudlelibrary.dao.ModelingScenarioTemp.FileInfo;
import com.example.moudlelibrary.dao.ModelingScenarioTemp.Authority;
import com.example.moudlelibrary.dao.ModelingScenarioTemp.PythonInterpreter;
import com.example.moudlelibrary.dao.ModelingScenarioTemp.WebResponse;
import com.example.moudlelibrary.tools.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.bson.json.JsonObject;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;

/**
 * Created by HeYuanqing on 2023/09/04  20:44
 **/
@CrossOrigin
@RestController
public class ModuleController {
    @Autowired
    private DockerService dockerService;
    @Autowired
    private WebSocketService webSocketService;
    @Autowired
    private MongoDBOperator mongoDBOperator;

//    private String rootPath = "/home/vge/DockerContainer/";
    private String rootPath = "E:\\DockerContainer\\";

    @GetMapping(value = "/getModuleInfo")
    public List<Module> getModuleInfo() {
        List<Module> modules = (List<Module>) mongoDBOperator.finAll(new Module());
        return modules;
    }

    /**
     * @param
     * @return: java.util.Map<java.lang.String, java.lang.String>
     * @Description: 获取元数据
     * @Author: HeYuanqing
     * @Date: 2023/11/8 15:38
     */
    @GetMapping(value = "/getDataScheme")
    public Map<String, String> getDataScheme() {
        Map<String, String> content = new HashMap<>();
        try {
            content.put("fileScheme", FileOperator.ReadTXTFile("E:\\PycharmProjects\\Python-based Open Modeling Framework\\ModuleLibrary\\FDS\\OMF_File.xml"));
            content.put("dataScheme", FileOperator.ReadTXTFile("E:\\PycharmProjects\\Python-based Open Modeling Framework\\ModuleLibrary\\FDS\\OMF_Data.xml"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return content;
    }

    @GetMapping(value = "/test")
    public String test() throws InterruptedException {
//        1.创建docker容器并启动
//        String container = dockerService.createContainer("python:3.8-slim-bullseye", "test", "F:\\workspace", "test");
        String containerID = "aa0f88bb60556ac18558ce0354614adc154b24009398e98ce4fcc3806ee90e37";
//        dockerService.startContainer("aa0f88bb60556ac18558ce0354614adc154b24009398e98ce4fcc3806ee90e37");
//        dockerService.runScript(containerID, "123456");
//        System.out.println(container);
        return containerID;
    }

    @PostMapping(value = "creatDockerContainer")
    public String creatDockerContainer(@RequestBody Map config) {
        if (config.isEmpty()) {
            return null;
        }
        String name = (String) config.get("name");
        String imageName = (String) config.get("imageName");
        // 验证容器是否存在
        DockerContainer container = (DockerContainer) mongoDBOperator.findOneByKey(new DockerContainer(), "name", name);
        if (container == null) {
//            不存在则创建
            container = new DockerContainer();
            container.setName(name);
            container.setId(UUID.randomUUID().toString());
            container.setImageName(imageName);
            container.setVolumesDir(rootPath + name);
            container.setWorkDir(name);
            String containerID = dockerService.createContainer(container.getImageName(), container.getName(), container.getVolumesDir(), container.getName());
            container.setContainerID(containerID);
            mongoDBOperator.insertDocument(container);
            System.out.println("Docker容器：" + container.getName() + " 创建成功！");
        }
        // 检查容器状态，如果容器处于关闭状态则启动启动容器
        try {
            if (!dockerService.checkState(container.getContainerID())) {
                dockerService.startContainer(container.getContainerID());
            }

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
//        dockerService.createContainer("python:3.8-slim-bullseye", "test", "F:\\workspace", "test");
        return container.getContainerID();
    }

    @PostMapping(value = "startDockerContainer")
    public WebResponse startDockerContainer(@RequestBody Map config) {
        String containerID = (String) config.get("containerID");
        try {
            if (!dockerService.checkState(containerID)) {
                dockerService.startContainer(containerID);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return WebResponse.success;
    }

    /**
     * @param info 建模场景基础信息
     * @return: java.lang.String
     * @Description: 构建建模场景
     * @Author: HeYuanqing
     * @Date: 2023/11/8 15:35
     */
    @PostMapping(value = "creatModelingScene")
    public String creatModelingScene(@RequestBody Map info) throws InterruptedException {
        ModelingScene modelingScene = (ModelingScene) mongoDBOperator.findOneByKey(new ModelingScene(), "name", (String) info.get("name"));
        if (modelingScene != null) {
            return modelingScene.getId();
        }
        modelingScene = new ModelingScene();
        String SceneID = UUID.randomUUID().toString();
        modelingScene.setId(SceneID);
        modelingScene.setName((String) info.get("name"));

        switch (info.get("interpreter").toString()) {
            case "Python3_7":
                modelingScene.setInterpreter(PythonInterpreter.Python3_7);
                break;
            case "Python3_8":
                modelingScene.setInterpreter(PythonInterpreter.Python3_8);
                break;
            case "Python3_9":
                modelingScene.setInterpreter(PythonInterpreter.Python3_9);
                break;
            default:
                return WebResponse.error.toString();
        }

        modelingScene.setDesc((String) info.get("desc"));
        modelingScene.setDeveloper((String) info.get("developer"));
        modelingScene.setEmail((String) info.get("email"));
        if (info.get("authority").equals("Public")) {
            modelingScene.setAuthority(Authority.PUBLIC);
        } else {
            modelingScene.setAuthority(Authority.PRIVATE);
        }

        modelingScene.setCode((String) info.get("code"));
        /** 构建容器 */
        String imageName = modelingScene.getInterpreter().getTag();
        String containerID = "";
        // 验证容器是否存在
        DockerContainer container = (DockerContainer) mongoDBOperator.findOneByKey(new DockerContainer(), "name", modelingScene.getName());

        if (container == null) {
            //不存在则创建
            container = new DockerContainer();
            container.setName(modelingScene.getName());
            container.setId(UUID.randomUUID().toString());
            container.setImageName(imageName);
            container.setVolumesDir(rootPath + modelingScene.getName());
            container.setWorkDir(modelingScene.getName());
            containerID = dockerService.createContainer(container.getImageName(), container.getName(), container.getVolumesDir(), container.getName());
            container.setContainerID(containerID);

            mongoDBOperator.insertDocument(container);
            System.out.println("Docker容器：" + container.getName() + " 创建成功！");
        }
        // 检查容器状态，如果容器处于关闭状态则启动启动容器
        try {
            if (!dockerService.checkState(container.getContainerID())) {
                dockerService.startContainer(container.getContainerID());
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        modelingScene.setContainerID(container.getContainerID());
        mongoDBOperator.insertDocument(modelingScene);

        // 构建初始化执行程序
        String pythonScriptContent = "def print_hi(name):\n"
                + "    print(f'Hello, {name}!')\n\n"
                + "if __name__ == '__main__':\n"
                + "    print_hi('word')";
        dockerService.writeFileToContainer(containerID,"main.py",pythonScriptContent);

//        添加工作空间
        dockerService.runCMD(containerID,"mkdir workspace");
        String command = "pip install requests";
        dockerService.runCMD(containerID, command);

        return SceneID;
    }

    /**
     * @param sceneID
     * @return: com.example.moudlelibrary.dao.ModelingScenarioTemp.WebResponse
     * @Description: 删除建模场景
     * @Author: HeYuanqing
     * @Date: 2024/6/4 17:15
     */
    @PostMapping(value = "deleteModelingScene")
    public WebResponse deleteModelingScene(@RequestBody Map sceneID) throws InterruptedException {
        // 从请求体中获取场景ID
        String id = (String) sceneID.get("id");

        // 根据ID查询建模场景，判断是否存在
        ModelingScene modelingScene = (ModelingScene) mongoDBOperator.findOneByKey(new ModelingScene(), "id", id);
        if (modelingScene == null) {
            System.out.println("ModelingScene: " + id + " not found！");
            // 如果场景不存在，返回错误响应
            return WebResponse.error;
        }

        // 获取建模场景关联的容器ID
        String containerID = modelingScene.getContainerID();
        // 删除关联的容器
        mongoDBOperator.delete(new DockerContainer(), "containerID", containerID);
        // 删除建模场景
        mongoDBOperator.delete(new ModelingScene(), "id", id);
        dockerService.removeContainer(containerID);
        // 删除成功，返回成功响应
        return WebResponse.success;
    }
    /**
     * @param sceneID 建模场景ID
     * @return: com.example.moudlelibrary.dao.ModelingScene
     * @Description: 获取建模场景信息
     * @Author: HeYuanqing
     * @Date: 2023/11/8 15:34
     */
    @PostMapping(value = "getModelingSceneInfo")
    public ModelingScene getModelingSceneInfo(@RequestBody Map sceneID) {
        String id = (String) sceneID.get("id");
        ModelingScene modelingScene = (ModelingScene) mongoDBOperator.findOneByKey(new ModelingScene(), "id", id);

        return modelingScene;
    }

    /**
     * @param
     * @return: java.util.List<com.example.moudlelibrary.dao.ModelingScene>
     * @Description: 获取所有建模场景信息
     * @Author: HeYuanqing
     * @Date: 2023/11/8 15:35
     */
    @GetMapping(value = "getModelingScenes")
    public List<ModelingScene> getModelingSceneInfos() {

        List<ModelingScene> modelingScenes = (List<ModelingScene>) mongoDBOperator.finAll(new ModelingScene());

        return modelingScenes;
    }

    /**
     * @param postData
     * @return: com.example.moudlelibrary.dao.ModelingScenarioTemp.WebResponse
     * @Description: 保存代码
     * @Author: HeYuanqing
     * @Date: 2023/11/8 15:36
     */
    @PostMapping(value = "saveCode")
    public WebResponse saveCode(@RequestBody Map postData) {
        String id = (String) postData.get("id");
        String code = (String) postData.get("code");
        if (mongoDBOperator.update(new ModelingScene(), id, "code", code)) {
            return WebResponse.success;
        } else {
            return WebResponse.error;
        }

    }

    /**
     * @param postData
     * @return: java.util.List<java.util.Map>
     * @Description: 获取依赖库信息
     * @Author: HeYuanqing
     * @Date: 2023/11/8 15:36
     */
    @PostMapping(value = "getLibraryInfo")
    public List<Map> getLibraryInfo(@RequestBody Map postData) throws InterruptedException, IOException {
        System.out.println("获取环境中的包信息");
        String containerID = (String) postData.get("containerID");
//        String projectID = (String) postData.get("projectID");
        String projectName = (String) postData.get("projectName");
        dockerService.runCMD(containerID, "pip list > library_list.txt");

        String path = rootPath + projectName + "/library_list.txt";
        FileInputStream fileInputStream = new FileInputStream(path);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
        String line;
        List<Map> pythonLibrary = new ArrayList<>();
        bufferedReader.readLine();
        bufferedReader.readLine();
        while ((line = bufferedReader.readLine()) != null) {
            String[] libPages = line.split(" ");
            Map libPage = new HashMap();
            libPage.put("name", libPages[0]);
            libPage.put("version", libPages[libPages.length - 1]);
            pythonLibrary.add(libPage);
        }
        fileInputStream.close();

        return pythonLibrary;
    }

    /**
     * @param postData
     * @return: com.example.moudlelibrary.tools.ResultMessage
     * @Description: python依赖库操作：添加和删除
     * @Author: HeYuanqing
     * @Date: 2023/11/8 15:37
     */
    @PostMapping(value = "packageOperation")
    public ResultMessage packageOperation(@RequestBody Map postData) throws InterruptedException {
        String containerID = (String) postData.get("containerID");
        String type = (String) postData.get("type");
        String name = (String) postData.get("name");
        String version = (String) postData.get("version");
        String command = "";
        switch (type) {
            case "add":
                if (version.equals("")) {
                    command = "pip install " + name;
                } else {
                    command = "pip install " + name + "==" + version;
                }
                break;
            case "remove":
                if (version.equals("")) {
                    command = "pip uninstall " + name + " -y";
                } else {
                    command = "pip uninstall " + name + "==" + version + " -y";
                }
                break;

        }
        ResultMessage message = dockerService.runCMD(containerID, command);
        return message;
    }

    /**
     * @param postData
     * @return: java.lang.String
     * @Description: 调用python脚本执行
     * @Author: HeYuanqing
     * @Date: 2023/11/8 15:37
     */
    @PostMapping(value = "/runPython")
    public String runPython(@RequestBody Map postData) throws InterruptedException {
        String containerID = (String) postData.get("containerID");
        String projectID = (String) postData.get("projectID");
        String projectName = (String) postData.get("projectName");
        String code = (String) postData.get("code");
        // 1. 检查容器状态，如果没有启动则启动
        if (!dockerService.checkState(containerID)) {
            dockerService.startContainer(containerID);
        }
        // 2.将运行脚本保存到容器运行空间
        FileOperator.WriteTXTFile(code, rootPath + projectName + "\\main.py");
        // 调用模型运行
        dockerService.runScript(containerID, projectID);
        return "代码成功执行";
    }


    @PostMapping("/getDataTree")
    public List<Map> getDataTree(@RequestBody Map postData) {
        System.out.println("查询目录树");
        String projectName = (String) postData.get("name");
        String path = rootPath + "\\" + projectName + "\\workspace";
        List<Map> fileTree = FileOperator.readFiles2EleTree(path);
        return fileTree;
    }
    @PostMapping("getCurDirFiles")
    List<FileInfo> getCurDirFiles(@RequestBody Map postData) {

        String storagePath = (String) postData.get("path");
        FileInfo fileInfo = FileInfo.builder().filePath(storagePath).build();
        List<FileInfo> filesTree;
        try {
            filesTree = FileOperator.getFileDirectoryTree(fileInfo, true, false);
        } catch (FileNotFoundException e) {
            System.out.println(storagePath + " 文件查询失败!");
            e.printStackTrace();
            return null;
        }
//        System.out.println("文件查询成功！");
        return filesTree;
    }
    @PostMapping("newFolder")
    WebResponse newFolder(@RequestBody Map fold) {
        //文件存储路径
        String storagePath = (String) fold.get("path");
        if (FileOperator.NewFolder(storagePath)) {
            System.out.println("文件夹：" + storagePath + " 已创建于 "  + " 个人存储空间！");
            return WebResponse.success;
        } else {
            return WebResponse.error;
        }
    }

    @PostMapping("deleteFiles")
    WebResponse deleteFiles(@RequestBody List<Map> files) {
        System.out.println("ok");
        for (int i = 0; i < files.size(); i++) {
            Map oriFile = files.get(i);
            FileOperator.deleteFiles(FileInfo.builder().filePath((String) oriFile.get("filePath")).build());
        }
        System.out.println("删除了 " + files.size() + " 个文件（文件夹）");
        return WebResponse.success;
    }

    @PostMapping(value = "/stopPython")
    public String stopPython(@RequestBody Map postData) throws InterruptedException {
        String containerID = (String) postData.get("containerID");
        String projectID = (String) postData.get("projectID");
        // 关闭运行的模型
        dockerService.stopScript(containerID, projectID);
        // 关闭容器
        dockerService.closeContainer(containerID);
        return "模型运行容器已关闭！";
    }

    @PostMapping(value = "/publishGeoserver")
    public WebResponse publishGeoserver(@RequestBody Map postData) {

        String workSpace = (String) postData.get("workSpace");
        String storeName = (String) postData.get("name");

        String layerName = (String) postData.get("name");
        String type = (String) postData.get("type");
        String styleName = "";
        switch (type) {
            case "border":
                styleName = "polygon_hollow_basic";
                break;
            case "river":
                styleName = "line";
                break;
            case "subbasin":
                styleName = "subBasinStyle";
                break;
        }
        String shapePath = (String) postData.get("path");

        if (GeoServerOperation.PublishShape(workSpace, storeName, layerName, styleName, shapePath)) {
            // 获取sceneID
            String sceneInfo = FileOperator.readTxtFile("E:\\DockerContainer\\" + workSpace + "\\sceneinfo.txt");
            JSONObject jsonObject = new JSONObject(sceneInfo);
            String id = jsonObject.getString("id");
            webSocketService.sendMessageByProject(id,"layer:" + layerName);
            return WebResponse.success;
        }
        return WebResponse.error;
    }

    @PostMapping("uploadData")
    WebResponse uploadData(@RequestParam MultipartFile multipartFile, @RequestParam String dataPath) {
//        String dataPath = (String) postParamdata.get("dataPath");
        //获取文件全称
        String fileName = multipartFile.getOriginalFilename();
        //获取文件类型
        String fileType = (fileName.substring(fileName.lastIndexOf("."))).substring(1);
        //获取文件名称
        String title = fileName.substring(0, fileName.lastIndexOf("."));
        //文件存储路径
        String storagePath =  dataPath;
        File fold = new File(storagePath);
        if (!fold.exists()) {
            fold.mkdirs();
        }
        try {
            File file = new File(fold, title + '.' + fileType);
            if (file.exists()) {
                System.out.println("文件已存在，上传失败");
                return WebResponse.error;
            }
            multipartFile.transferTo(file);

        } catch (IOException e) {
            System.out.println("文件上传失败");
            e.printStackTrace();
            return WebResponse.error;
        }
        System.out.println("文件：" + fileName + " 已上传至 "  + " 个人存储空间！");
        return WebResponse.success;
    }

    @PostMapping(value = "/importModels2Library")
    WebResponse importModels2Library(@RequestBody Map postData) {
        String ip = (String) postData.get("ip");

        ModelLibrary modelLibrary = (ModelLibrary) mongoDBOperator.findOneByKey(new ModelLibrary(), "ip", ip);
       if (modelLibrary != null){
           return WebResponse.error;
       }
        modelLibrary = new ModelLibrary();
        modelLibrary.setId(UUID.randomUUID().toString());
        modelLibrary.setIp(ip);
        modelLibrary.setDeveloper((String) postData.get("developer"));
        modelLibrary.setEmail((String) postData.get("email"));
        modelLibrary.setModels((List<Map>) postData.get("models"));
        mongoDBOperator.insertDocument(modelLibrary);
//        try {
//            Thread.sleep(3000);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }

        return WebResponse.success;
    }

    @GetMapping(value = "/getModels")
    List<Map> getModels() {
        List<Map> models = new ArrayList<>();
        List<ModelLibrary> modelLibraries = (List<ModelLibrary>) mongoDBOperator.finAll(new ModelLibrary());
        for (ModelLibrary modelLibrary : modelLibraries) {
            models.addAll(modelLibrary.getModels());
        }
        return models;
    }

    @PostMapping(value = "/updateModelLibraries")
    WebResponse updateModelLibraries(@RequestBody Map postData) throws CustomHttpRequestException, JsonProcessingException {
        String ip = (String) postData.get("ip");
        ModelLibrary modelLibrary = (ModelLibrary) mongoDBOperator.findOneByKey(new ModelLibrary(), "ip", ip);
        if (modelLibrary == null){
            return WebResponse.error;
        }
        String modelsData = HttpUtils.GET(ip + "get-models-info");
        ObjectMapper objectMapper = new ObjectMapper();
        List<Map> models = objectMapper.readValue(modelsData, new TypeReference<List<Map>>() {});
        mongoDBOperator.update(new ModelLibrary(),modelLibrary.getId(),"models", models);

        return WebResponse.success;
    }
    @GetMapping(value = "/getModelInfo/{modelName}")
    Map getModelInfo(@PathVariable("modelName") String modelName) throws CustomHttpRequestException {

        Map result = new HashMap();
        List<ModelLibrary> modelLibraries = (List<ModelLibrary>) mongoDBOperator.finAll(new ModelLibrary());
        for (ModelLibrary modelLibrary : modelLibraries) {
            for (Map model: modelLibrary.getModels()
                 ) {
                Map attribute = (Map) model.get("attribute_info");
                if (attribute.get("name").equals(modelName)){
                    result.put("ip",modelLibrary.getIp());
                    result.put("input_info",model.get("input_info"));
                    result.put("output_info",model.get("output_info"));
                    result.put("operation_info",model.get("operation_info"));
                    return result;
                }
            }

        }

        return null;
    }
}
